Skip to content

fix: MentionOnly mode retains channel context and memory capture#533

Merged
jamiepine merged 3 commits intomainfrom
fix/mention-only-context-awareness
Apr 4, 2026
Merged

fix: MentionOnly mode retains channel context and memory capture#533
jamiepine merged 3 commits intomainfrom
fix/mention-only-context-awareness

Conversation

@jamiepine
Copy link
Copy Markdown
Member

@jamiepine jamiepine commented Apr 4, 2026

Summary

  • Channel-settings MentionOnly was behaving identically to binding-level require_mention — suppressed messages were invisible to the agent on its next turn
  • Now MentionOnly injects suppressed messages into the in-memory LLM context window, so the agent stays context-aware when eventually @mentioned
  • MentionOnly now also runs passive memory capture (previously skipped), matching Quiet mode behavior
  • Added UI helper text on both binding forms explaining the difference: binding-level blocks entirely, channel-level MentionOnly lets the agent see everything but only respond to mentions

Test plan

  • Set channel to MentionOnly via channel settings, send several messages without mentioning the bot, then @mention — verify the agent can reference the prior unmentioned messages
  • Set binding-level require_mention, send messages without mentioning — verify the agent cannot see them at all
  • Verify memory persistence works in MentionOnly (check working memory after suppressed messages)
  • Check binding UI shows helper text below the require_mention checkbox on Discord bindings

🤖 Generated with Claude Code

Note

AI-generated summary: This fix changes how MentionOnly mode works at the channel level. Instead of completely suppressing unmentioned messages from the agent (like binding-level require_mention does), MentionOnly now keeps them in the agent's context window while preventing automatic responses. The agent sees the conversation flow and memory accumulates passively, creating a more aware but selectively-responsive bot. UI improvements clarify the distinction between these two levels of mention enforcement.

Written by Tembo for commit 99467d2. This will update automatically on new commits.

Channel-settings-level MentionOnly was behaving identically to
binding-level require_mention — suppressed messages were invisible to
the agent. Now MentionOnly injects suppressed messages into the
in-memory LLM context window and runs passive memory capture, so the
agent stays context-aware and only skips responding.

Also adds UI descriptions explaining the difference between binding-level
require_mention (blocks messages entirely) and channel-level MentionOnly
(agent sees everything, only responds to mentions).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 4, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2e002caa-3a2a-4674-bfd5-cf21eecb335e

📥 Commits

Reviewing files that changed from the base of the PR and between d421b03 and f07cecc.

📒 Files selected for processing (2)
  • interface/src/components/ChannelEditModal.tsx
  • interface/src/components/ChannelSettingCard.tsx
✅ Files skipped from review due to trivial changes (2)
  • interface/src/components/ChannelSettingCard.tsx
  • interface/src/components/ChannelEditModal.tsx

Walkthrough

Clarifies distinction between binding-level require_mention (blocks unmentioned messages at routing) and channel-level MentionOnly (ingests unmentioned messages for context but suppresses responses). UI copy/layout updated for require_mention; backend docs and agent suppression logic updated so MentionOnly suppressed inputs are recorded into conversation history and compaction is invoked.

Changes

Cohort / File(s) Summary
Discord UI components
interface/src/components/ChannelEditModal.tsx, interface/src/components/ChannelSettingCard.tsx
Restructured require_mention checkbox markup (wrap label around input), added explanatory paragraph under the checkbox in both components.
Conversation settings UI
interface/src/components/ConversationSettingsPanel.tsx
Updated mention_only description text to note messages remain visible to the agent for context while responses are limited to mentions/replies/commands.
Backend: message suppression logic
src/agent/channel.rs
For ResponseMode::MentionOnly, suppressed inputs are written into self.state.history (batched and single-message paths), compaction is invoked after injection, and message_count + check_memory_persistence() are updated/called before returning.
Backend: configuration/docs
src/config/types.rs, src/conversation/settings.rs
Rewrote doc comments: Binding.require_mention now documented as routing-level blocking (agent cannot see blocked messages); ResponseMode::MentionOnly documented as ingesting unmentioned messages into history/context while suppressing LLM turns.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: MentionOnly mode now retains channel context and performs memory capture, which directly corresponds to the primary modifications across the codebase.
Description check ✅ Passed The description is well-related to the changeset, explaining both the problem (MentionOnly behaving like require_mention) and the solution (injecting suppressed messages into context and enabling memory capture), with test plan and UI improvements documented.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/mention-only-context-awareness

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/agent/channel.rs (2)

1431-1447: Suppress-path behavior is duplicated; extract to a shared helper.

The MentionOnly history injection + passive memory capture flow is now duplicated in two places. Centralizing it avoids future drift between batched and single-message handling.

♻️ Suggested refactor sketch
+    async fn handle_suppressed_capture(
+        &mut self,
+        suppressed_user_texts: &[String],
+        message_increment: usize,
+    ) {
+        if matches!(self.resolved_settings.response_mode, ResponseMode::MentionOnly) {
+            let mut history = self.state.history.write().await;
+            for suppressed_user_text in suppressed_user_texts {
+                history.push(rig::message::Message::User {
+                    content: OneOrMany::one(UserContent::text(suppressed_user_text)),
+                });
+            }
+        }
+        self.message_count += message_increment;
+        self.check_memory_persistence().await;
+    }

Also applies to: 1859-1873

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/agent/channel.rs` around lines 1431 - 1447, Extract the duplicate
"suppress-path" behavior (injecting MentionOnly entries into in-memory history
and performing passive memory capture) into a shared helper and call it from
both places where it currently appears; specifically, create a helper function
(e.g., inject_mention_only_and_capture) that takes &mut self, an iterator over
pending_batch_entries (or a single formatted_text), and message_count, performs
the history.push into self.state.history.write().await for
ResponseMode::MentionOnly, increments self.message_count, and calls
self.check_memory_persistence().await; then replace the duplicated blocks that
check for ResponseMode::MentionOnly and update
message_count/check_memory_persistence with calls to this helper (references:
ResponseMode::MentionOnly, pending_batch_entries, state.history, message_count,
check_memory_persistence).

1444-1447: Please add targeted tests for the new MentionOnly async behavior.

Given the runtime behavior change in suppression paths, add regression tests for both batched and single-message flows to verify history ingestion and passive memory capture triggering.

As per coding guidelines: “For changes in async/stateful paths (worker lifecycle, cancellation, retrigger, recall cache behavior), include explicit race/terminal-state reasoning in the PR summary and run targeted tests in addition to just gate-pr.”

Also applies to: 1870-1873

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/agent/channel.rs` around lines 1444 - 1447, Add regression tests covering
the new MentionOnly async suppression behavior: write unit/integration tests
that exercise both batched and single-message flows to assert history ingestion
occurs and passive memory capture is triggered (i.e., message_count increments
and check_memory_persistence() is invoked) when ChannelMode::MentionOnly is
used; target the async/stateful paths exercised by the code that updates
self.message_count and calls check_memory_persistence(), and include assertions
that ensure no suppression bypasses these calls and that behavior matches Quiet
mode for passive capture. Ensure tests simulate relevant async timing/race
conditions (awaits, multiple concurrent messages) and cover the other affected
lines (around the similar block at 1870-1873) to prevent regressions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@interface/src/components/ChannelEditModal.tsx`:
- Around line 573-581: The checkbox label in ChannelEditModal (the input bound
to bindingForm.require_mention and updated via setBindingForm) is not associated
with its input for accessibility; add an explicit association by giving the
checkbox an id (e.g., requireMentionCheckbox) and reference that id from the
label's htmlFor attribute, or wrap the input element inside the label element so
clicking the text toggles the checkbox and screen readers correctly link the
label to the input.

In `@interface/src/components/ChannelSettingCard.tsx`:
- Around line 1175-1183: The checkbox label in ChannelSettingCard isn’t
associated with its input; update the JSX so the label is programmatically bound
to the input (either give the input a unique id and set the label’s htmlFor to
that id, or wrap the <input> with the <label>) for the checkbox controlling
bindingForm.require_mention; ensure the onChange and checked props remain
unchanged and that the id is unique within the component to preserve
accessibility and click-target behavior.

---

Nitpick comments:
In `@src/agent/channel.rs`:
- Around line 1431-1447: Extract the duplicate "suppress-path" behavior
(injecting MentionOnly entries into in-memory history and performing passive
memory capture) into a shared helper and call it from both places where it
currently appears; specifically, create a helper function (e.g.,
inject_mention_only_and_capture) that takes &mut self, an iterator over
pending_batch_entries (or a single formatted_text), and message_count, performs
the history.push into self.state.history.write().await for
ResponseMode::MentionOnly, increments self.message_count, and calls
self.check_memory_persistence().await; then replace the duplicated blocks that
check for ResponseMode::MentionOnly and update
message_count/check_memory_persistence with calls to this helper (references:
ResponseMode::MentionOnly, pending_batch_entries, state.history, message_count,
check_memory_persistence).
- Around line 1444-1447: Add regression tests covering the new MentionOnly async
suppression behavior: write unit/integration tests that exercise both batched
and single-message flows to assert history ingestion occurs and passive memory
capture is triggered (i.e., message_count increments and
check_memory_persistence() is invoked) when ChannelMode::MentionOnly is used;
target the async/stateful paths exercised by the code that updates
self.message_count and calls check_memory_persistence(), and include assertions
that ensure no suppression bypasses these calls and that behavior matches Quiet
mode for passive capture. Ensure tests simulate relevant async timing/race
conditions (awaits, multiple concurrent messages) and cover the other affected
lines (around the similar block at 1870-1873) to prevent regressions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0db78488-4bab-4f4a-b643-5ebc249bffae

📥 Commits

Reviewing files that changed from the base of the PR and between 955efd3 and 99467d2.

📒 Files selected for processing (6)
  • interface/src/components/ChannelEditModal.tsx
  • interface/src/components/ChannelSettingCard.tsx
  • interface/src/components/ConversationSettingsPanel.tsx
  • src/agent/channel.rs
  • src/config/types.rs
  • src/conversation/settings.rs

- Add compaction check after injecting suppressed messages into history
  in MentionOnly mode to prevent unbounded context growth (tembo)
- Drop history write lock before async compaction/memory calls
- Add "or given a command" to MentionOnly docs and UI description (tembo)
- Wrap checkbox inputs inside label elements for accessibility (coderabbitai)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@interface/src/components/ChannelSettingCard.tsx`:
- Line 1184: Update the Mention Only helper text in ChannelSettingCard
(component ChannelSettingCard, the paragraph currently saying "Blocks messages
entirely — the agent won't see them at all. To let the agent read all messages
but only respond to mentions, use Mention Only mode in Channels settings (cog
icon).") to clarify that Mention Only still allows command invocations in
addition to mentions — e.g., change the copy to indicate the agent will only
reply to `@mentions` or slash/command invocations while still reading all
messages, so users can distinguish channel-level Mention Only from binding-level
require_mention.

In `@src/agent/channel.rs`:
- Around line 1431-1453: Avoid awaiting compaction/persistence on the
suppression fast-path: instead of awaiting
self.compactor.check_and_compact().await and
self.check_memory_persistence().await inline (inside the
ResponseMode::MentionOnly/suppression branch after pushing pending_batch_entries
and incrementing self.message_count), spawn a detached background task to run
those checks so the suppression path remains non-blocking; clone/move the
minimal pieces needed into the task (e.g., Arc::clone(&self) or
self.compactor.clone()) and call compactor.check_and_compact().await and
self.check_memory_persistence().await inside tokio::spawn (or equivalent) so
errors can be logged but do not block the channel.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cd90689e-bcab-498b-8897-5a876cd30ff6

📥 Commits

Reviewing files that changed from the base of the PR and between 99467d2 and d421b03.

📒 Files selected for processing (5)
  • interface/src/components/ChannelEditModal.tsx
  • interface/src/components/ChannelSettingCard.tsx
  • interface/src/components/ConversationSettingsPanel.tsx
  • src/agent/channel.rs
  • src/conversation/settings.rs
✅ Files skipped from review due to trivial changes (2)
  • src/conversation/settings.rs
  • interface/src/components/ChannelEditModal.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • interface/src/components/ConversationSettingsPanel.tsx

The binding-level helper text now mentions commands, mentions, and
replies — matching the actual MentionOnly trigger behavior.

The compaction await is kept inline: check_and_compact() is cheap
(read lock + token estimate) and spawns workers for real compaction,
consistent with existing non-suppression call sites.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@jamiepine jamiepine merged commit 2195e35 into main Apr 4, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant